Skip to content

Fix Monaco editor initialization regression#57

Merged
beNative merged 1 commit intomainfrom
codex/fix-monaco-text-editor-visibility
Oct 4, 2025
Merged

Fix Monaco editor initialization regression#57
beNative merged 1 commit intomainfrom
codex/fix-monaco-text-editor-visibility

Conversation

@beNative
Copy link
Copy Markdown
Owner

@beNative beNative commented Oct 4, 2025

Summary

  • add a shared Monaco loader helper that waits for the AMD loader before initializing
  • update the code and diff editors to use the loader and guard against uninitialized APIs

Testing

  • npm run build

https://chatgpt.com/codex/tasks/task_e_68e125d25e448332805b34b0b4f8756f

@beNative beNative merged commit 1e7d55a into main Oct 4, 2025
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting

Comment thread components/CodeEditor.tsx
Comment on lines 221 to +246
useEffect(() => {
if (editorRef.current && typeof ((window as any).require) !== 'undefined') {
// Configure Monaco Environment to load workers from CDN. This is crucial for syntax highlighting.
if (!(window as any).MonacoEnvironment) {
(window as any).MonacoEnvironment = {
getWorkerUrl: function (_moduleId: any, label: string) {
const CDN_PATH = 'https://cdn.jsdelivr.net/npm/monaco-editor@0.44.0/min/vs';
if (label === 'json') return `${CDN_PATH}/language/json/json.worker.js`;
if (label === 'css' || label === 'scss' || label === 'less') return `${CDN_PATH}/language/css/css.worker.js`;
if (label === 'html' || label === 'handlebars' || label === 'razor') return `${CDN_PATH}/language/html/html.worker.js`;
if (label === 'typescript' || label === 'javascript') return `${CDN_PATH}/language/typescript/ts.worker.js`;
return `${CDN_PATH}/editor/editor.worker.js`;
},
};
let isCancelled = false;

const initializeEditor = async () => {
if (!editorRef.current) {
return;
}

(window as any).require.config({ paths: { 'vs': 'https://cdn.jsdelivr.net/npm/monaco-editor@0.44.0/min/vs' }});
(window as any).require(['vs/editor/editor.main'], () => {
if (editorRef.current) {
// Ensure any previous instance is disposed
if (monacoInstanceRef.current) {
disposeEditorShortcuts();
monacoInstanceRef.current.dispose();
}
try {
const monacoApi = await ensureMonaco();
if (!monacoApi || isCancelled || !editorRef.current) {
return;
}

const editorInstance = monaco.editor.create(editorRef.current, {
value: content,
language: language || 'plaintext',
theme: theme === 'dark' ? 'vs-dark' : 'vs',
automaticLayout: true,
fontSize: computedFontSize,
fontFamily: computedFontFamily,
minimap: {
enabled: true,
},
wordWrap: 'on',
folding: true,
showFoldingControls: 'always',
bracketPairColorization: {
enabled: true,
},
});
monacoApiRef.current = monacoApi;

editorInstance.onDidChangeModelContent(() => {
const currentValue = editorInstance.getValue();
if (currentValue !== contentRef.current) {
onChange(currentValue);
}
});
if (monacoInstanceRef.current) {
disposeEditorShortcuts();
monacoInstanceRef.current.dispose();
}

editorInstance.onDidScrollChange((e: any) => {
if (e.scrollTopChanged) {
onScroll?.({
scrollTop: e.scrollTop,
scrollHeight: e.scrollHeight,
clientHeight: editorInstance.getLayoutInfo().height
});
}
});
const editorInstance = monacoApi.editor.create(editorRef.current, {
value: content,
language: language || 'plaintext',
theme: theme === 'dark' ? 'vs-dark' : 'vs',
automaticLayout: true,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Initialize editor with latest props when loader resolves

The async loader now waits for ensureMonaco() before creating the editor, but the initialization closure captures content, language, and theme from the first render and these props are not part of the effect’s dependency list. If the component mounts with placeholder values (e.g., empty string) and the real content/language/theme arrive before the Monaco loader finishes downloading, the editor will be created with the stale initial props and the subsequent effects ([content], [language], [theme]) never rerun because they already executed earlier when monacoInstanceRef.current was still null. This leaves the editor showing outdated content or incorrect language/theme until those props change again. Use the ref values (contentRef.current) or include the props in the effect dependencies so the editor initializes with the most recent data.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant